import {
  animationFrameScheduler,
  asyncScheduler,
  delay,
  delayWhen,
  dematerialize,
  fromEvent,
  interval,
  map,
  materialize,
  merge,
  NextNotification,
  Observable,
  observeOn,
  of,
  subscribeOn,
  take,
  tap,
  throwError,
  timeInterval,
  timeout,
  timestamp,
  toArray,
} from "rxjs";

export const timestampDemo = () => {
  const clickWithTimestamp = fromEvent(document, "click").pipe(timestamp());

  // Emits data of type { value: PointerEvent, timestamp: number }
  clickWithTimestamp.subscribe((data) => {
    console.log(data); // {value： PonterEvent, timestamp: 当前时间的时间缀}
  });
};
export const timeIntervalDemo = () => {
  const intervalObs = interval(500);

  intervalObs.pipe(timeInterval()).subscribe((x) => console.log(x));

  // 把 发出的数据转成对象
  // { value: 0, interval: 1000 }
  // { value: 1, interval: 1000 }
  // { value: 2, interval: 1000 }
};

// subscribeOn 和 observeOn 的区别是什么?
export const subscribeOnDemo2 = () => {
  // 把 subscribe
  console.log("begin start");

  // observeOn 把初使化变成了异步，只是subscribe 这个过程的做成了异步了;
  const obs = new Observable((observe) => {
    console.log("observabel inner start");
    observe.next(1);
    observe.next(2);
    console.log("observabel inner end");
  });

  obs.pipe(subscribeOn(asyncScheduler)).subscribe((x) => console.log(x));

  console.log("endend!!!");

  // bengin start
  // endend!!!
  //observabel inner start
  // 1
  // 2
  // observabel inner end
};

export const observeOnDemo2 = () => {
  console.log("begin start");

  // observeOn 把初使化变成了同步,构造函数内部先执行，先调用next也只是暂存，把subscribe 这个过程的做成了异步了;
  const obs = new Observable((observe) => {
    console.log("observabel inner start");
    observe.next(1);
    observe.next(2);
    console.log("observabel inner end");
  });

  obs.pipe(observeOn(asyncScheduler)).subscribe((x) => console.log(x));

  console.log("endend!!!");

  // bengin start
  //observabel inner start
  // observabel inner end
  // endend!!!
  // 1
  // 2
};

export const subscribeOnDemo = () => {
  const sourceA = of(1, 2, 3).pipe(subscribeOn(asyncScheduler)); // 创建一个异步调试器
  const sourceB = of(4, 5, 6);

  merge(sourceA, sourceB).subscribe((x) => console.log(x));
  // 4,5,6,1,2,3 把 sourceA 使用异步调试器
};

// 这里要对调试器清楚了?
export const observeOnDemo = () => {
  const someDiv = document.createElement("div");
  someDiv.style.cssText = "width: 200px;background: #09c";
  document.body.appendChild(someDiv);
  const intervals = interval(10); // Intervals are scheduled
  // with async scheduler by default...
  intervals
    .pipe(
      observeOn(animationFrameScheduler), // ...but we will observe on animationFrame
    ) // scheduler to ensure smooth animation.
    .subscribe((val) => {
      someDiv.style.height = val + "px";
    });
};

export const delayWhenDemo = () => {
  const click$ = fromEvent(document, "click");

  click$
    .pipe(delayWhen(() => interval(Math.random() * 5000))) // 这个点击事件在 0 - 5 s 内触发，是随产生的
    .subscribe((x) => {
      console.log(x);
    });
};

export const toArrayDemo = () => {
  const intervalObs = interval(1000);

  // 把拿到的 10 个数放到一个数组里面，做一次输出
  intervalObs.pipe(take(10), toArray()).subscribe((x) => {
    console.log(x); //  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 一次输出
  });
};

class MyCustomerTimeOutError extends Error {
  constructor() {
    super("this is my error");
    this.name = "MyCustomerTimeOutError";
  }
}

export const timeoutDemo2 = () => {
  const slow$ = interval(2000);

  slow$
    .pipe(
      timeout({
        each: 1000,
        // 对超时的处理
        with: () => throwError(() => new MyCustomerTimeOutError()),
      }),
    )
    .subscribe({
      next(e) {
        console.log(e);
      },

      error(err) {
        console.log(err);
      },
    });
};

export const timeoutDemo = () => {
  console.log("this is timeout test");

  const slow$ = interval(2000);

  const first$ = interval(900);

  slow$
    .pipe(
      timeout({
        each: 1000,
        with: () => first$,
      }),
    )
    .subscribe((x) => {
      console.log(x);
    });
};

export const materializeDemo = () => {
  console.log("jsut a test");

  const testDataObs = of("a", "b", 1, 2, 3, "d");

  testDataObs
    .pipe(
      map((d) => {
        if (typeof d === "string") {
          return d.toUpperCase();
        } else {
          return d;
        }
      }),
      materialize(), // 有个相对的是dematerialize
    )
    .subscribe((x) => {
      // 打印出来的结果如下: 把每个值转成了对象
      console.log(JSON.stringify(x));
      /**
{"kind":"N","value":"A","hasValue":true}
utils-operators.ts:20 {"kind":"N","value":"B","hasValue":true}
utils-operators.ts:20 {"kind":"N","value":1,"hasValue":true}
utils-operators.ts:20 {"kind":"N","value":2,"hasValue":true}
utils-operators.ts:20 {"kind":"N","value":3,"hasValue":true}
utils-operators.ts:20 {"kind":"N","value":"D","hasValue":true}
utils-operators.ts:20 {"kind":"C","hasValue":false}
      */
    });
};

export const dematerializeDemo = () => {
  const nextNotification1: NextNotification<string> = {
    kind: "N",
    value: "A",
  };

  const nextNotification2: NextNotification<string> = {
    kind: "N",
    value: "B",
  };

  const nextNotification3: NextNotification<string> = {
    kind: "E",
    error: new TypeError("this is type error"),
  };

  const ofObs = of(nextNotification1, nextNotification2, nextNotification3);

  ofObs.pipe(dematerialize()).subscribe({
    next(value) {
      console.log(value);
    },
    error(err) {
      console.log(err);
    },
  });
};

export function delayDemo() {
  const click$ = fromEvent(document, "click");

  // 把click 事件延迟1s在执行
  click$.pipe(delay(1000)).subscribe((x) => {
    console.log(x);
  });
}

// tap 主要是处理一些副作用
export function tapDemo() {
  // of(Math.random())
  //   .pipe(
  //     tap(console.log),
  //     map((n) => (n > 0.5 ? "big" : "small")),
  //   )
  //   .subscribe(console.log);
  //
  const source = of(1, 2, 3, 4, 5);

  source
    .pipe(
      tap((n) => {
        if (n > 3) {
          throw new TypeError(`Value ${n} is greater than 3`);
        }
      }),
    )
    .subscribe({ next: console.log, error: (err) => console.log(err.message) });
}
